W projekcie wykorzystujemy informacje zebrane podczas gry w Affective SpaceShooter 2.
W pierwszym etapie analizujemy dane pod kątem zależności między wynikiem gry a poszczególnymi cechami osobowości.
W drugiej części dokonujemy uczenia nadzorowanego - na podstawie zbioru cech osobowości staramy się przewidzieć średni wynik punktowy gry reprezentowany przez klasy "low" i "medium".
import pandas as pd
import numpy as np
import csv
md = pd.read_csv("BIRAFFE-metadata.csv", sep=';')
#usunięcie tych rekordów gdzie osoba nie ma danych z gry w space
md = md[pd.notnull(md['SPACE'])]
md = md[pd.notnull(md['OPENNESS'])]
#zostawienie id tych osób, bo pliki mają w nazwie id
ids = md['ID'].values
import csv
import json
print("start")
pd.set_option('display.max_columns', 500)
#import metadata
data = pd.read_csv("merged_scores.csv", sep=',')
data = data[pd.notnull(data['OPENNESS'])]
data = data[pd.notnull(data['CONSCIENTIOUSNESS'])]
data = data[pd.notnull(data['EXTRAVERSION'])]
data = data[pd.notnull(data['AGREEABLENESS'])]
data = data[pd.notnull(data['NEUROTICISM'])]
data=data[data.Score == 'GameOver']
#data.head(25)
type(data)
mean_c=[]
with open('mean_scores.csv', 'w', newline='') as csvfile:
#nazwy kolumn- wszystkie z plików json
fieldnames = ["P_ID","OPENNESS","CONSCIENTIOUSNESS","EXTRAVERSION","AGREEABLENESS","NEUROTICISM","Mean"]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
#ustawienie nagłówków
writer.writeheader()
for my_id in ids:
data1=data.loc[data['P_ID'] == my_id]
nr=data1.shape[0]
#print(data1)
score=data1['Value'].sum()
if (score):
mean = score/nr
#print(mean)
new_data={ 'P_ID': data1['P_ID'].iloc[0], 'OPENNESS': data1['OPENNESS'].iloc[0], 'CONSCIENTIOUSNESS': data1['CONSCIENTIOUSNESS'].iloc[0],'EXTRAVERSION': data1['EXTRAVERSION'].iloc[0],'AGREEABLENESS': data1['AGREEABLENESS'].iloc[0],'NEUROTICISM': data1['NEUROTICISM'].iloc[0],'Mean': mean}
writer.writerow(new_data)
Obliczamy średni wynik gry dla każdej osoby, który będziemy zestawiać z cechami osobowości.
import matplotlib.pyplot as plt
import plotly.express as px
import seaborn as sns
import pandas as pd
import numpy as np
import csv
pd.set_option('display.max_columns', 500)
#import metadata
data = pd.read_csv("mean_scores.csv", sep=',')
data.head()
fig = px.scatter(data, x = 'OPENNESS', y = 'Mean', title='test')
fig.show()
fig1 = px.scatter(data, x = 'CONSCIENTIOUSNESS', y = 'Mean', title='test')
fig1.show()
fig2 = px.scatter(data, x = 'EXTRAVERSION', y = 'Mean', title='test')
fig2.show()
fig3 = px.scatter(data, x = 'AGREEABLENESS', y = 'Mean', title='test')
fig3.show()
fig4 = px.scatter(data, x = 'NEUROTICISM', y = 'Mean', title='test')
fig4.show()
Powyższe wykresy przedstawiają korelację średniego wyniku gry z każdą z cech osobowości. Można z nich wywnioskować, że zależności nie istnieją.
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots(figsize=(5,5))
sns.heatmap(data.corr(), vmax=1.0, center=0, fmt='.2f', linewidths=.9, annot=True,cbar_kws={"shrink": .70})
plt.show();
Powyższe zestawienie również pokazuje, że korelacja między badanymi elementami jest niska.
data.boxplot(column=['OPENNESS', 'CONSCIENTIOUSNESS', 'EXTRAVERSION', 'AGREEABLENESS', 'NEUROTICISM'], rot=45)
data.boxplot(column=['Mean'], rot=45)
print("Większość wyników znajduje się pomiędzy około 400 a 1100 punktów, grupa o średniej <=2000 to najbardziej wiarygodna grupa testowa")
data_trimmed=data.loc[data['Mean'] <= 2000]
fig, ax = plt.subplots(figsize=(5,5))
sns.heatmap(data_trimmed.corr(), vmax=1.0, center=0, fmt='.2f', linewidths=.9, annot=True,cbar_kws={"shrink": .70})
plt.show();
Zestawienie korelacji dla danych testowych, w których średni wynik gry wyniósł <= 2000 punktów. Korelacja między badanymi elementami nadal jest niska.
pd.set_option('display.max_columns', 500)
#import metadata
data = pd.read_csv("mean_scores.csv", sep=',')
data.head()
fig = px.scatter(data_trimmed, x = 'OPENNESS', y = 'Mean', title='test')
fig.show()
fig1 = px.scatter(data_trimmed, x = 'CONSCIENTIOUSNESS', y = 'Mean', title='test')
fig1.show()
fig2 = px.scatter(data_trimmed, x = 'EXTRAVERSION', y = 'Mean', title='test')
fig2.show()
fig3 = px.scatter(data_trimmed, x = 'AGREEABLENESS', y = 'Mean', title='test')
fig3.show()
fig4 = px.scatter(data_trimmed, x = 'NEUROTICISM', y = 'Mean', title='test')
fig4.show()
Niską korelację potwierdzają również wykresy.
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn import linear_model as ln
def showBarPlot(X, Y, title):
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)
regressor = ln.LinearRegression()
regressor.fit(X_train, y_train)
print(regressor.score(X_train, y_train))
print(regressor.intercept_)
print(regressor.coef_)
y_pred = regressor.predict(X_test)
df1 = pd.DataFrame({'Actual': y_test, 'Predicted': y_pred})
df2 = df1.head(25)
df2.plot(kind='bar', figsize=(10,5))
plt.grid(which='major', linestyle='-', linewidth='0.5', color='green')
plt.grid(which='minor', linestyle=':', linewidth='0.5', color='black')
plt.show()
print(title)
print('Mean Absolute Error:', metrics.mean_absolute_error(y_test, y_pred))
print('Mean Squared Error:', metrics.mean_squared_error(y_test, y_pred))
print('Root Mean Squared Error:', np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
print('\n\n')
X = data[['OPENNESS']].values
Y = data['Mean'].values
showBarPlot(X,Y,'OPENNESS - Mean')
X = data[['CONSCIENTIOUSNESS']].values
Y = data['Mean'].values
showBarPlot(X,Y,'CONSCIENTIOUSNESS - Mean')
X = data[['EXTRAVERSION']].values
Y = data['Mean'].values
showBarPlot(X,Y,'EXTRAVERSION - Mean')
X = data[['AGREEABLENESS']].values
Y = data['Mean'].values
showBarPlot(X,Y,'AGREEABLENESS - Mean')
X = data[['NEUROTICISM']].values
Y = data['Mean'].values
showBarPlot(X,Y,'NEUROTICISM - Mean')
Niska korelacja wpływa na złe wyniki modelu uczonego za pomocą regresji liniowej, który szuka zależności między średnim wynikiem a poszczególnymi cechami osobowości.
X = data[['OPENNESS']].values
Y = data['CONSCIENTIOUSNESS'].values
showBarPlot(X,Y,'OPENNESS - CONSCIENTIOUSNESS')
X = data[['OPENNESS']].values
Y = data['EXTRAVERSION'].values
showBarPlot(X,Y,'OPENNESS - EXTRAVERSION')
X = data[['OPENNESS']].values
Y = data['AGREEABLENESS'].values
showBarPlot(X,Y,'OPENNESS - AGREEABLENESS')
X = data[['OPENNESS']].values
Y = data['NEUROTICISM'].values
showBarPlot(X,Y,'OPENNESS - NEUROTICISM')
X = data[['CONSCIENTIOUSNESS']].values
Y = data['OPENNESS'].values
showBarPlot(X,Y,'CONSCIENTIOUSNESS - OPENNESS')
X = data[['CONSCIENTIOUSNESS']].values
Y = data['EXTRAVERSION'].values
showBarPlot(X,Y,'CONSCIENTIOUSNESS - EXTRAVERSION')
X = data[['CONSCIENTIOUSNESS']].values
Y = data['AGREEABLENESS'].values
showBarPlot(X,Y,'CONSCIENTIOUSNESS - AGREEABLENESS')
X = data[['CONSCIENTIOUSNESS']].values
Y = data['NEUROTICISM'].values
showBarPlot(X,Y,'CONSCIENTIOUSNESS - NEUROTICISM')
X = data[['EXTRAVERSION']].values
Y = data['OPENNESS'].values
showBarPlot(X,Y,'EXTRAVERSION - OPENNESS')
X = data[['EXTRAVERSION']].values
Y = data['CONSCIENTIOUSNESS'].values
showBarPlot(X,Y,'EXTRAVERSION - CONSCIENTIOUSNESS')
X = data[['EXTRAVERSION']].values
Y = data['AGREEABLENESS'].values
showBarPlot(X,Y,'EXTRAVERSION - AGREEABLENESS')
X = data[['EXTRAVERSION']].values
Y = data['NEUROTICISM'].values
showBarPlot(X,Y,'EXTRAVERSION - NEUROTICISM')
X = data[['AGREEABLENESS']].values
Y = data['OPENNESS'].values
showBarPlot(X,Y,'AGREEABLENESS - OPENNESS')
X = data[['AGREEABLENESS']].values
Y = data['CONSCIENTIOUSNESS'].values
showBarPlot(X,Y,'AGREEABLENESS - CONSCIENTIOUSNESS')
X = data[['AGREEABLENESS']].values
Y = data['EXTRAVERSION'].values
showBarPlot(X,Y,'AGREEABLENESS - EXTRAVERSION')
X = data[['AGREEABLENESS']].values
Y = data['NEUROTICISM'].values
showBarPlot(X,Y,'AGREEABLENESS - NEUROTICISM')
X = data[['NEUROTICISM']].values
Y = data['OPENNESS'].values
showBarPlot(X,Y,'NEUROTICISM - OPENNESS')
X = data[['NEUROTICISM']].values
Y = data['CONSCIENTIOUSNESS'].values
showBarPlot(X,Y,'NEUROTICISM - CONSCIENTIOUSNESS')
X = data[['NEUROTICISM']].values
Y = data['EXTRAVERSION'].values
showBarPlot(X,Y,'NEUROTICISM - EXTRAVERSION')
X = data[['NEUROTICISM']].values
Y = data['AGREEABLENESS'].values
showBarPlot(X,Y,'NEUROTICISM - AGREEABLENESS')
Niska korelacja wpływa również na złe wyniki modelu uczonego za pomocą regresji liniowej, który szuka zależności między poszczególnymi cechami osobowości.
Z analizy wynika, że nie ma szczególnych zależności między pojedynczymi cechami osobowości a wynikiem gry.
Rozpatrzymy zatem cały zbiór cech i dokonamy na nich uczenia nadzorowanego - predykcji wyniku gry w klasach "low" i "medium".
Dokonujemy porównania różnych modeli.
from sklearn.neural_network import MLPClassifier
from sklearn.neighbors import KNeighborsClassifier,NearestCentroid
from sklearn.svm import SVC
from sklearn.gaussian_process import GaussianProcessClassifier
from sklearn.gaussian_process.kernels import RBF
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.naive_bayes import GaussianNB, MultinomialNB
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis
from sklearn import linear_model
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn import preprocessing
from sklearn.metrics import classification_report, f1_score
import random
data = pd.read_csv("mean_scores.csv", sep=',')
data.head()
def mapping(x):
if x < data['Mean'].median() :
return 'low'
else:
return 'high'
data['Mean_Class'] = data['Mean'].map(lambda x: mapping(x));
X = data[['OPENNESS', 'CONSCIENTIOUSNESS', 'EXTRAVERSION', 'AGREEABLENESS', 'NEUROTICISM']].values
y = data['Mean_Class'].values
names = ["Nearest Centroid ", #przypisuje do obserwacji etykietę klasy próbek treningowych, których średnia jest najbliższa obserwacji
"Nearest Neighbors", #zależność między zmiennymi objaśniającymi a objaśnianymi jest złożona lub nietypowa
"Linear SVC", #Support Vector Classification
"Gaussian Process", #bazuje na aproksymacji Laplace'a - dwie klasy
"Decision Tree",
"Random Forest",
"Naive Bayes",#Naiwne klasyfikatory bayesowskie są oparte na założeniu o wzajemnej niezależności predyktorów
"Logistic Regression"] #zmienna zależna przyjmuje tylko dwie wartości
classifiers = [
NearestCentroid(),
KNeighborsClassifier(3),
SVC(kernel="linear", C=0.025),
GaussianProcessClassifier(1.0 * RBF(1.0)),
DecisionTreeClassifier(max_depth=5),
RandomForestClassifier(max_depth=5, n_estimators=10, max_features=1),
GaussianNB(),
linear_model.LogisticRegression(solver='lbfgs')]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42, shuffle=True)
#randomowy przydzial
y_pred = np.empty((y_test.size),dtype = '<U6' )
for i in range (0,y_test.size):
y_pred[i] = random.choice(['low','high'])
print(classification_report(y_test, y_pred))
print("Średni wynik randomowego przydziału oscyluje ok.0.5")
print("")
for name, classifier in zip(names, classifiers):
classifier.fit(X_train, y_train)
# wyniki walidacji krzyzowej dla danego estymatora - określają jakość modelu
# Testowanie każdego podzbioru używając pozostałych jako zbiór treningowy.
# cv - określa krotność walidacji - przy niedużych zbiorach najczęściej k=10
scores = cross_val_score(classifier, X, y, cv=10).tolist()
print(name)
print("Walidacja krzyżowa:", np.mean(scores))
predictions = classifier.predict(X_test)
print(classification_report(y_test, predictions))
print("")
Wyniki walidacji krzyżowej przekraczają 0.5, zatem można uznać, że modele całkiem dobrze radzą sobie z predykcją. Najlepsze wyniki osiągają modele: "Linear SVM", "Decision Tree" oraz "Logistic Regression".
F₁ jest kolejną miarą dokładności testu. W tym przypadku najlepsze wyniki osiągają modele: "Nearest Neighbors" oraz "Nearest Centroid".
Biorąc pod uwagę średnią obu wartości, najlepszym modelem okazuje się "Nearest Neighbors".
print("Porównanie wyników przy zmianie liczby sąsiadów:")
xx =[]
yy = []
for i in [1,2,3,5,10,15,20,30,40,50]:
classifier = KNeighborsClassifier(i)
classifier.fit(X_train, y_train)
scores = cross_val_score(classifier, X, y, cv=10).tolist()
predictions = classifier.predict(X_test)
xx.append(np.mean(scores))
yy.append(np.mean(f1_score(y_test, predictions, average=None)))
labels = [1,2,3,5,10,15,20,30,40,50]
def plot_bar_x():
index = np.arange(len(labels))
plt.bar(index, xx)
plt.xlabel('Liczba sąsiadów')
plt.xticks(index, labels, fontsize=10)
plt.show()
def plot_bar_y():
index = np.arange(len(labels))
plt.bar(index, yy)
plt.xlabel('Liczba sąsiadów')
plt.xticks(index, labels, fontsize=10)
plt.show()
print("")
print("Zmiana liczby sąsiadów a wartość walidacji krzyżowej:")
plot_bar_x()
print("Zmiana liczby sąsiadów a wartość F1:")
plot_bar_y()
Z wykresów wynika, że najoptymalniejsza liczba sąsiadów znajduje się w przedziale <2,10>, ponieważ w obu przypadkach jakość modelu jest na wysokim poziomie.